package com.fish.compiler.parse;

import com.dny.asmtop.CB;
import com.dny.asmtop.Command;
import com.fish.antlr.JFishBaseListener;
import com.fish.antlr.JFishLexer;
import com.fish.antlr.JFishParser;
import com.fish.compiler.lang.*;
import com.fish.compiler.parse.domain.JFishLangVariable;
import org.antlr.v4.runtime.tree.TerminalNode;

import java.util.*;

/**
 * Created by jlutt on 2018-01-20.
 *
 * @author jlutt
 */
public class JFishTreeWalkListener extends JFishBaseListener {

  private CompilationUnit compilationUnit;

  private ClassDeclaration classDeclaration;

  Map<String, JFishLangVariable> variables = new HashMap<>();

  List<ScopeInstruction> commandQueue = new ArrayList<>();

  @Override
  public void exitVariable(JFishParser.VariableContext ctx) {
    final TerminalNode varName = ctx.ID();

    final JFishParser.ValueContext varValue = ctx.value();
    final int varType = varValue.getStart().getType();
    final String varTextValue = varValue.getText();
    final int varIndex = variables.size();

    JFishLangVariable var = new JFishLangVariable(varIndex, varType, varTextValue);
    variables.put(varName.getText(), var);
    commandQueue.add(new VariableDeclaration(var));

    logVariableDeclarationStatementFound(varName, varValue);
  }

  @Override
  public void exitPrint(JFishParser.PrintContext ctx) {
    final TerminalNode varName = ctx.ID();
    final boolean printedVarNotDeclared = !variables.containsKey(varName.getText());
    if (printedVarNotDeclared) {
      final String erroFormat = "ERROR: WTF? You are trying to print var '%s' which has not been declared!!!111. ";
      System.out.printf(erroFormat, varName.getText());
      return;
    }
    final JFishLangVariable variable = variables.get(varName.getText());

    commandQueue.add(new PrintVariable(variable));

//    if (variable.getType() == JFishLexer.NUMBER) {
//      commandQueue.add(CB.call(CB.fieldStatic(System.class, "out"), "println", CB.value(Integer.valueOf(variable.getValue()))));
//    } else if (variable.getType() == JFishLexer.STRING) {
//      commandQueue.add(CB.call(CB.fieldStatic(System.class, "out"), "println", CB.value(variable.getValue())));
//    }
    logPrintStatementFound(varName, variable);
  }

  @Override
  public void exitCompilationUnit(JFishParser.CompilationUnitContext ctx) {
    super.enterCompilationUnit(ctx);
    compilationUnit = new CompilationUnit(classDeclaration);
  }

  @Override
  public void exitClassDeclaration(JFishParser.ClassDeclarationContext ctx) {
    super.enterClassDeclaration(ctx);

    //获取类名
    final String className = ctx.className().getText();
    classDeclaration = new ClassDeclaration(commandQueue, className);
  }

  private void logVariableDeclarationStatementFound(TerminalNode varName, JFishParser.ValueContext varValue) {
    final int line = varName.getSymbol().getLine();

    final String format = "OK: You declared variable named '%s' with value of '%s' on type '%s' at line '%s'.\n";
    System.out.printf(format, varName, varValue.getText(), varValue.getStart().getType(), line);
  }

  private void logPrintStatementFound(TerminalNode varName, JFishLangVariable variable) {
    final int line = varName.getSymbol().getLine();
    final String format = "OK: You instructed to print variable '%s' which has value of '%s' at line '%s'.'\n";
    System.out.printf(format, variable.getId(), variable.getValue(), line);
  }

  public CompilationUnit getCompilationUnit() {
    return compilationUnit;
  }
}
